/**
 * Copyright (c) 2024 watereyes
 * workflow is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan
 * PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *          http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */
package com.koron.common.web.util;

import org.swan.bean.MessageBean;
import org.swan.excel.Excel;

import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;

import org.koron.ebs.mybatis.SessionFactory;
import org.koron.ebs.mybatis.TaskAnnotation;
import org.koron.ebs.util.field.EnumElement;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.http.HttpEntity;
import org.springframework.web.servlet.ModelAndView;

import com.google.gson.Gson;
import com.koron.beetl.FieldGroupFunction;
import com.koron.common.bean.CrossBean;
import com.koron.common.bean.DefineFieldBean;
import com.koron.common.bean.IdentityBean;
import com.koron.common.bean.query.BaseQueryBean;
import com.koron.util.Constant;
import com.koron.util.Tools;

/**
 * 实现基础的CRUD功能
 * 
 * @author swan
 * 
 */
public class CRUD {
	/**
	 * 列表方式展示数据列表
	 * 
	 * @param crud 实现CRUD接口
	 * @param bean 查询BEAN
	 * @param tag 执行方法的标签,参看{@link TaskAnnotation}
	 * @return spring视图
	 */
	public static final ModelAndView list(ICrud crud, BaseQueryBean bean, String tag, String... param) {
		String viewName = null;
		String[] layers = new String[9];
		if (param != null) {
			for (int i = 0; i < param.length; i++) {
				if (param[i] == null || param[i].isEmpty())
					continue;
				if (i == 0)
					viewName = param[i];
				else if (i > 0 && i < 10)
					layers[i - 1] = param[i];
			}
		}

		ModelAndView view = Tools.getView(viewName == null ? (crud.getActionKey() + ".btl") : viewName, crud.getClass());
		try (SessionFactory factory = new SessionFactory()) {
			List<?> list = factory.runTask(crud, tag, List.class, bean, view);
			view.addObject("datalist", list);
			view.addObject("condition", new Gson().toJson(bean));

			view.addObject("layer", FieldGroupFunction.getFieldBean(crud.getLayer()));
			for (int i = 1; i < layers.length; i++) {
				if (layers[i] == null)
					continue;
				view.addObject("layer" + i, FieldGroupFunction.getFieldBean(layers[i]));
			}
			view.addObject("flag", new HashMap<String, String>());

			return view;
		}
	}

	@SuppressWarnings("unchecked")
	public static final ModelAndView cross(ICrud crud, BaseQueryBean bean, String tag, CrossConfigBean config) {
		ModelAndView view = Tools.getView(crud.getActionKey() + ".btl", crud.getClass());
		view.addObject("flag", new HashMap<String, String>());
		try (SessionFactory factory = new SessionFactory()) {
			HashMap<String, Object> map = cross(factory.runTask(crud, tag, List.class, bean, view), config, crud.getLayer());
			view.addObject("datalist", map.get("datalist"));
			view.addObject("layer", map.get("layer"));
			view.addObject("condition", new Gson().toJson(bean));
			return view;
		}
	}

	/**
	 * AJAX方式获取数据列表
	 * 
	 * @param crud 实现CRUD接口
	 * @param bean 查询BEAN
	 * @param tag 执行方法的标签,参看{@link TaskAnnotation}
	 * @return json数据
	 */
	public static final String listajax(ICrud crud, BaseQueryBean bean, String tag) {
		ModelAndView view = new ModelAndView();
		try (SessionFactory factory = new SessionFactory()) {
			List<?> list = factory.runTask(crud, tag, List.class, bean, view);
			List<Object> data = new ArrayList<>();
			String layer = crud.getLayer();
			FieldGroupFunction function = new FieldGroupFunction();
			for (Object o : list) {
				data.add(function.call(new Object[] { layer, o }, null));
			}
			HashMap<String, Object> map = new HashMap<>();
			map.putAll(view.getModel());
			map.put("datalist", data);
			map.put("data", list);
			map.put("condition", bean);
			map.put("code", "0");
			return new Gson().toJson(map);
		}
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static final String listajaxCross(ICrud crud, BaseQueryBean bean, String tag, CrossConfigBean config) {
		try (SessionFactory factory = new SessionFactory()) {
			List<CrossBean> list = factory.runTask(crud, tag, List.class, bean, null);
			List<Object> data = new ArrayList<>();
			HashMap<String, Object> crossMap = cross(list, config, crud.getLayer());

			FieldGroupFunction function = new FieldGroupFunction();
			for (Object o : (Collection) crossMap.get("datalist")) {
				data.add(function.call(new Object[] { crossMap.get("layer"), o }, null));
			}

			HashMap<String, Object> map = new HashMap<>();
			map.put("datalist", data);
			map.put("data", crossMap.get("datalist"));
			map.put("condition", bean);
			return new Gson().toJson(map);
		}
	}

	/**
	 * 获取明细功能
	 * 
	 * @param crud 实现CRUD接口
	 * @param pojo 查询BEAN
	 * @param tag 执行方法的标签,参看{@link TaskAnnotation}
	 * @param param 参数,可为null,第一个为视图名，第2-11个为层别
	 * @return spring视图
	 */
	public static final ModelAndView detail(ICrud crud, IdentityBean pojo, String tag, String... param) {
		String viewName = null;
		String[] layers = new String[9];
		if (param != null) {
			for (int i = 0; i < param.length; i++) {
				if (param[i] == null || param[i].isEmpty())
					continue;
				if (i == 0)
					viewName = param[i];
				else if (i > 0 && i < 10)
					layers[i - 1] = param[i];
			}
		}
		ModelAndView view = Tools.getView(viewName == null ? (crud.getActionKey() + "pre.btl") : viewName, crud.getClass());
		Object ret = pojo;
		if (pojo.getId() != -1) {
			ret = new SessionFactory().runTask(crud, tag, pojo.getClass(), pojo, view);
		}
		view.addObject("bean", ret);
		view.addObject("flag", new HashMap<String, String>());
		for (int i = 1; i < layers.length; i++) {
			if (layers[i] == null)
				continue;
			view.addObject("layer" + i, FieldGroupFunction.getFieldBean(layers[i]));
		}
		return view;
	}

	/**
	 * <pre>
	 * 存盘功能 
	 * id为-1则insert,否则update
	 * </pre>
	 * 
	 * @param crud crud接口
	 * @param pojo 包含ID的POJO对象
	 * @param insertTag 执行插入的标签,参看{@link TaskAnnotation}
	 * @param updateTag 执行更新的标签,参看{@link TaskAnnotation}
	 * @return 消息BEAN
	 */
	public static final MessageBean<Integer> save(ICrud crud, IdentityBean pojo, String insertTag, String updateTag) {
		Integer ret = 0;
		String item = "添加";
		if (pojo.getId() == -1) {
			ret = new SessionFactory().runTask(crud, insertTag, Integer.class, pojo);
		} else {
			ret = new SessionFactory().runTask(crud, updateTag, Integer.class, pojo);
			item = "修改";
		}
		MessageBean<Integer> msg = new MessageBean<Integer>();
		msg.setData(ret);
		if (ret == null) {
			msg.setCode(Constant.MESSAGE_DBFAIL);
			msg.setDescription("数据" + item + "失败。");
		} else if (ret > 0) {
			msg.setCode(0);
			msg.setDescription("已" + item + ret + "条数据。");
		} else {
			msg.setCode(Constant.MESSAGE_DBFAIL);
			msg.setDescription("数据" + item + "失败。");
		}
		return msg;
	}

	/**
	 * 直接执行某个写操作(insert delete updae)
	 * 
	 * @param crud crud接口
	 * @param pojo 包含ID的POJO对象
	 * @param tag 执行方法的标签,参看{@link TaskAnnotation}
	 * @return 消息BEAN
	 */
	public static final MessageBean<Integer> execute(ICrud crud, Object[] pojo, String tag, String item) {
		Integer ret = 0;
		try (SessionFactory factory = new SessionFactory()) {
			ret = factory.runTask(crud, tag, Integer.class, Arrays.asList(pojo));
			MessageBean<Integer> msg = new MessageBean<Integer>();
			msg.setData(ret);
			if (ret == null) {
				msg.setCode(Constant.MESSAGE_DBFAIL);
				msg.setDescription("数据" + item + "失败。");
			} else if (ret > 0) {
				msg.setCode(0);
				msg.setDescription("已" + item + ret + "条数据。");
			} else {
				msg.setCode(Constant.MESSAGE_DBFAIL);
				msg.setDescription("数据" + item + "失败。");
			}
			return msg;
		}
	}

	/**
	 * 删除功能
	 * 
	 * @param crud crud接口
	 * @param id 删除数据的ID
	 * @param tag 执行方法的标签,参看{@link TaskAnnotation}
	 * @return
	 */
	public static final MessageBean<Integer> delete(ICrud crud, int id, String tag) {
		Integer ret = 0;
		try (SessionFactory factory = new SessionFactory()) {
			ret = factory.runTask(crud, tag, Integer.class, id);
			MessageBean<Integer> msg = new MessageBean<Integer>();
			msg.setData(ret);
			if (ret == null) {
				msg.setCode(Constant.MESSAGE_DBFAIL);
				msg.setDescription("数据删除失败。");
			} else if (ret == 1) {
				msg.setCode(0);
				msg.setDescription("已删除" + ret + "条数据。");
			} else {
				msg.setCode(Constant.MESSAGE_DBFAIL);
				msg.setDescription("数据删除失败。");
			}
			return msg;
		}
	}

	/**
	 * 批量删除功能
	 * 
	 * @param crud crud接口
	 * @param ids 删除数据的ID
	 * @param tag 执行方法的标签,参看{@link TaskAnnotation}
	 * @return
	 */
	public static final MessageBean<Integer> delete(ICrud crud, List<Integer> ids, String tag) {
		Integer ret = 0;
		try (SessionFactory factory = new SessionFactory()) {
			ret = factory.runTask(crud, tag, Integer.class, ids);
			MessageBean<Integer> msg = new MessageBean<Integer>();
			msg.setData(ret);
			if (ret == null) {
				msg.setCode(Constant.MESSAGE_DBFAIL);
				msg.setDescription("数据删除失败。");
			} else if (ret == ids.size()) {
				msg.setCode(0);
				msg.setDescription("已删除" + ret + "条数据。");
			} else {
				msg.setCode(Constant.MESSAGE_DBFAIL);
				msg.setDescription("数据删除失败。");
			}
			return msg;
		}
	}
	/**
	 * 导出EXCEL文件
	 * @param crud 调用的实例
	 * @param exportFileName 导出的文件名
	 * @param excelTemplate excel的模板文件
	 * @param bean 查询用的类
	 * @param key 把数据解析成excel的类
	 * @param tags 查询用任务的标签
	 * @return
	 */
	public static final HttpEntity<?> export(ICrud crud,String exportFileName,String excelTemplate, BaseQueryBean bean, final String key, String... tags) {
		bean.setPage(0);
		bean.setPageCount(99999);
		final HashMap<String, Object> map = new HashMap<>();
		try (SessionFactory factory = new SessionFactory()) {
			for (String tag : tags) {
				Object dataList = factory.runTask(crud, tag, Object.class, bean, null);
				map.put(tag, dataList);
			}
			try {
				return Excel.createFile(exportFileName, Tools.getWebFile(excelTemplate).openStream(), key, map);
			} catch (IOException ex) {
				ex.printStackTrace();
			}
			return null;
		}
	}

	public static class CrossConfigBean {
		/**
		 * 定义交叉的列，如果为NULL则根据实际情况进行交叉，否则根据这值进行交叉
		 */
		private LinkedHashMap<Object, String> columns;
		/**
		 * 标识是同一行的数据的字段名字
		 */
		private String key;
		/**
		 * 要交叉的列
		 */
		private String crossField;
		/**
		 * 交叉的值
		 */
		private String[] valueString;

		/**
		 * @return 获取定义交叉的列，如果为NULL则根据实际情况进行交叉，否则根据这值进行交叉
		 */
		public LinkedHashMap<Object, String> getColumns() {
			return columns;
		}

		/**
		 * @param columns 设置定义交叉的列 ，如果为NULL则根据实际情况进行交叉，否则根据这值进行交叉
		 */
		public void setColumns(LinkedHashMap<Object, String> columns) {
			this.columns = columns;
		}

		/**
		 * @return 获取标识是同一行的数据
		 */
		public String getKey() {
			return key;
		}

		/**
		 * @param key 设置标识是同一行的数据
		 */
		public void setKey(String key) {
			this.key = key;
		}

		/**
		 * @return 获取要交叉的列
		 */
		public String getCrossField() {
			return crossField;
		}

		/**
		 * @param crossField 设置要交叉的列
		 */
		public void setCrossField(String crossField) {
			this.crossField = crossField;
		}

		/**
		 * @return 获取交叉的值
		 */
		public String[] getValueString() {
			return valueString;
		}

		/**
		 * @param valueString 设置交叉的值
		 */
		public void setValueString(String[] valueString) {
			this.valueString = valueString;
		}

		/**
		 * @param enumKey 枚举key值 设置定义交叉的列，如果为NULL则根据实际情况进行交叉，否则根据这值进行交叉
		 */
		public void setColumns(String enumKey) {
			EnumElement<Object> enums = Tools.getEnumByKey(enumKey);
			setColumns((LinkedHashMap<Object, String>) enums.getItem());
		}
	}

	public static final HashMap<String, Object> cross(List<CrossBean> list, CrossConfigBean config, String layer) {
		List<DefineFieldBean> beans = FieldGroupFunction.getFieldBean(layer);
		// 原始数据
		LinkedHashMap<Object, String> columns = new LinkedHashMap<>();
		LinkedHashMap<String, CrossBean> map = new LinkedHashMap<String, CrossBean>();
		// 如果没指定交叉的列，则从数据中取到交叉的列
		if (config.getColumns() == null) {
			for (CrossBean crossBean : list) {
				BeanMap bm = BeanMap.create(crossBean);
				String column = String.valueOf(bm.get(config.getCrossField()));
				columns.put(column, column);
			}
		} else {
			for (Entry<Object, String> entry : config.getColumns().entrySet()) {
				columns.put(entry.getKey(), entry.getValue());
			}
		}

		// 给字段增加上交叉的列
		List<DefineFieldBean> tmps = new ArrayList<DefineFieldBean>();
		for (int i = 0; i < beans.size(); i++) {
			DefineFieldBean defineBean = beans.get(i);
			if (defineBean.getName().equals(config.getCrossField())) {
				Set<Entry<Object, String>> sets = columns.entrySet();
				for (Entry<Object, String> set : sets) {
					DefineFieldBean tmp = defineBean.clone();
					tmp.setCaption(set.getValue());
					tmp.setName(config.getValueString()[0] + String.valueOf(set.getKey()));
					tmps.add(tmp);
				}
			} else
				tmps.add(defineBean);
		}
		beans = tmps;
		// 依次从原始数据中取出数据并放进map中进行分行处理
		for (CrossBean crossBean : list) {
			BeanMap bm = BeanMap.create(crossBean);
			if (!map.containsKey(String.valueOf(bm.get(config.getKey())))) {
				// 如果处理的数据不存在
				map.put(String.valueOf(bm.get(config.getKey())), crossBean);
				Set<Entry<Object, String>> sets = columns.entrySet();
				for (Entry<Object, String> set : sets) {
					crossBean.put(config.getValueString()[0] + String.valueOf(set.getKey()), "");
				}
			}
			setValue(config, bm, map.get(bm.get(config.getKey())));
			System.out.println("");
		}
		HashMap<String, Object> ret = new HashMap<String, Object>();
		ret.put("datalist", map.values());
		ret.put("layer", beans);
		return ret;
	}

	/**
	 * @param config 交叉配置
	 * @param bm 原始BEAN
	 * @param target 转换到了目标BEAN
	 */
	private static final void setValue(CrossConfigBean config, BeanMap bm, CrossBean target) {
		target.put(config.getValueString()[0] + bm.get(config.getCrossField()), bm.get(config.getValueString()[0]));
	}

}
